package cn.buk.tms.train.dao;

import cn.buk.tms.dao.AbstractDao;
import cn.buk.tms.dto.TrainOrderPassengerDto;
import cn.buk.tms.entity.order.train.TrainOrder_;
import cn.buk.tms.factory.OperationLogFactory;
import cn.buk.tms.common.constant.TmsOrderConstant;
import cn.buk.tms.entity.misc.OperationLog;
import cn.buk.tms.entity.misc.PaymentMethod;
import cn.buk.tms.entity.misc.Supplier;
import cn.buk.tms.entity.order.train.TrainOrder;
import cn.buk.tms.train.sc.TrainOrderSearchCriteria;
import cn.buk.common.util.DateUtil;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

import jakarta.persistence.criteria.*;
import jakarta.transaction.Transactional;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

import static cn.buk.tms.common.constant.TmsOrderConstant.OPERATING_OBJECT_TRAIN_ORDER;

@Transactional
@Component
public class TrainOrderDaoImpl extends AbstractDao implements TrainOrderDao {

  private static final Logger logger = LogManager.getLogger(TrainOrderDaoImpl.class);

  /**
   * 保存酒店订单
   *
   * @param o
   * @return
   */
  @Override
  public int createTrainOrder(TrainOrder o) {
    em.persist(o);

    OperationLog log = OperationLogFactory.createTrainOrderLog();
    log.setEnterpriseId(o.getEnterpriseId());
    log.setOrderId(o.getId());
    log.setOperator(o.getOperator());
    log.setContent("创建订单");

    em.persist(log);

    return o.getId();
  }

  @Override
  public int createTrainOrders(List<TrainOrder> list) {
    for (TrainOrder o : list) {
      em.persist(o);

      OperationLog log = OperationLogFactory.createTrainOrderLog();
      log.setEnterpriseId(o.getEnterpriseId());
      log.setOrderId(o.getId());
      log.setOperator(o.getOperator());
      log.setContent("批量创建订单");

      em.persist(log);
    }

    return list.size();
  }

  @Override
  public TrainOrder getTrainOrderById(int id) {
    return em.find(TrainOrder.class, id);
  }

  @Override
  public TrainOrder getTrainOrderByOrderNo(int enterpriseId, String orderNo) {
    List<TrainOrder> trainOrders = em.createQuery("select o from TrainOrder o where o.enterpriseId = :enterpriseId and o.orderNo = :orderNo", TrainOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("orderNo", orderNo)
            .getResultList();
    return trainOrders.size() == 1 ? trainOrders.get(0) : null;
  }

  private List<Predicate> buildPredicates_searchTrainOrders(CriteriaBuilder cb, Root<TrainOrder> root,
                                                            int enterpriseId, TrainOrderSearchCriteria sc, List<Integer> allowedCustomerIds) {

    List<Predicate> predicates = new ArrayList<>();

    Predicate predicate = cb.conjunction();
    predicates.add(predicate);

    predicate = cb.equal(root.get(TrainOrder_.enterpriseId), enterpriseId);
    predicates.add(predicate);

    if (sc.getCustomerId() == 0) {
      // 散客
      predicate = cb.equal(root.get(TrainOrder_.customerType), 0);
      predicates.add(predicate);
    } else if (sc.getCustomerId() > 0) {
      // 指定客户
      predicate = cb.equal(root.get(TrainOrder_.customer), sc.getCustomerId());
      predicates.add(predicate);
    }

    if (sc.getBeginDate() != null && sc.getEndDate() != null) {
      predicate = cb.greaterThanOrEqualTo(root.get(TrainOrder_.createTime), sc.getBeginDate());
      predicate = cb.and(predicate,
              cb.lessThanOrEqualTo(root.get(TrainOrder_.createTime), sc.getEndDate()));
      predicates.add(predicate);
    } else if (sc.getBeginDate() != null) {
      predicate = cb.greaterThanOrEqualTo(root.get(TrainOrder_.createTime), sc.getBeginDate());
      predicates.add(predicate);
    } else if (sc.getEndDate() != null) {
      predicate = cb.lessThanOrEqualTo(root.get(TrainOrder_.createTime), sc.getEndDate());
      predicates.add(predicate);
    }

    if (sc.getName() != null && sc.getName().length() > 0) {
      // 入住人姓名
      predicate = cb.like(root.get(TrainOrder_.passengerName), "%" + sc.getName() + "%");
      predicates.add(predicate);
    }

    if (sc.getDepartureDate() != null && sc.getDepartureDate().length() == 10) {
      // 出发日期
      try {
        predicate = cb.equal(root.get(TrainOrder_.ddate),
                DateUtil.convertToDate(sc.getDepartureDate(), "yyyy-MM-dd"));
        predicates.add(predicate);
      } catch (ParseException e) {
        logger.error(e.getMessage());
      }
    }

    if (sc.getOrderNo().length() > 0) {
      predicate = cb.equal(root.get(TrainOrder_.orderNo), sc.getOrderNo());
      predicates.add(predicate);
    }

//      if (sc.getStatus() > -1) {
//        predicate = cb.equal(root.get(TrainOrder_.status), sc.getStatus());
//        predicates.add(predicate);
//      }
    //默认不查找“已取消”的订单
    if (sc.getStatus() != -1) {
      predicate = cb.equal(root.get(TrainOrder_.status), sc.getStatus());
    } else {
      predicate = cb.notEqual(root.get(TrainOrder_.status), TmsOrderConstant.ORDER_STATUS_CANCELED);
    }
    predicates.add(predicate);

    if (sc.getOp1() != null && sc.getOp1().trim().length() > 0) {
      predicate = cb.equal(root.get(TrainOrder_.operator), sc.getOp1());
      predicates.add(predicate);
    }

    if (allowedCustomerIds != null && allowedCustomerIds.size() > 0) {
      // 只允许查看指定的企业客户的订单
      predicate = cb.in(root.get(TrainOrder_.customer).get("id")).value(allowedCustomerIds);
      predicates.add(predicate);
    }

    return predicates;
  }

  /**
   * search train orders
   *
   * @param enterpriseId
   * @param sc
   * @param allowedCustomerIds
   * @return
   */
  @Override
  public List<TrainOrder> searchTrainOrders(int enterpriseId, TrainOrderSearchCriteria sc, List<Integer> allowedCustomerIds) {
    List<TrainOrder> results = null;

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<TrainOrder> cq = cb.createQuery(TrainOrder.class);
    Root<TrainOrder> root = cq.from(TrainOrder.class);
    root.alias("o");

    var predicates0 = buildPredicates_searchTrainOrders(cb, root, enterpriseId, sc, allowedCustomerIds);


    //计算根据条件查询得出的数据总数
    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root<TrainOrder> countRoot = countQuery.from(TrainOrder.class);
    countRoot.alias("o");

    var predicates1 = buildPredicates_searchTrainOrders(cb, countRoot, enterpriseId, sc, allowedCustomerIds);

    // count items
    countQuery.select(cb.count(countRoot))
            .where(predicates1.toArray(new Predicate[predicates1.size()]));
    Long count = em.createQuery(countQuery).getSingleResult();

    //查询
    cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

    List<Order> orderByes = new ArrayList<>();
    orderByes.add(cb.desc(root.get("id")));

    cq.orderBy(orderByes);

    int maxResults = count.intValue();
    if (maxResults > 0) {
      sc.getPage().setRowCount(maxResults);

      results = em.createQuery(cq)
              .setFirstResult(sc.getPage().getFirstPosition())
              .setMaxResults(sc.getPage().getPageSize())
              .getResultList();

//            for (FlightOrder order : results) {
//                order.getFlights().size();
//                order.getPassengers().size();
//                order.getExtras().size();
//            }
    }

    if (results == null) {
      results = new ArrayList<>();
    }

    return results;
  }

  /**
   * 修改酒店订单备注
   *
   * @param enterpriseId
   * @param username
   * @param orderId
   * @param remark
   * @return
   */
  @Override
  public int updateTrainOrderRemark(final int enterpriseId, final String username, final int orderId, final String remark) {
    TrainOrder o = em.find(TrainOrder.class, orderId);
    if (o.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("TrainOrder is not existed.");
    }

    o.setRemark(remark);
    em.merge(o);

    OperationLog log = OperationLogFactory.createTrainOrderLog();
    log.setEnterpriseId(o.getEnterpriseId());
    log.setOrderId(o.getId());
    log.setOperator(username);

    String content = "修改备注为: " + remark;
    log.setContent(content);
    em.persist(log);

    return 1;
  }

  @Override
  public int updateTrainOrderExternalOrderNo(int enterpriseId, String username, int orderId, String externalOrderNo) {
    TrainOrder o = em.find(TrainOrder.class, orderId);
    if (o.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("TrainOrder is not existed.");
    }

    o.setExternalOrderNo(externalOrderNo);
    em.merge(o);

    OperationLog log = OperationLogFactory.createTrainOrderLog();
    log.setEnterpriseId(o.getEnterpriseId());
    log.setOrderId(o.getId());
    log.setOperator(username);

    String content = "修改外部订单号为: " + externalOrderNo;
    log.setContent(content);
    em.persist(log);

    return 1;
  }

  /**
   * 修改酒店订单的供应商
   *
   * @param enterpriseId
   * @param username
   * @param orderId
   * @param supplierId
   * @return
   */
  @Override
  public int updateTrainOrderSupplier(final int enterpriseId, final String username, final int orderId, final int supplierId) {
    TrainOrder o = em.find(TrainOrder.class, orderId);
    if (o.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("TrainOrder is not existed.");
    }

    Supplier supplier = em.find(Supplier.class, supplierId);
    if (supplier.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("Supplier is not existed.");
    }

    o.setSupplier(supplier);
    em.merge(o);

    OperationLog log = OperationLogFactory.createTrainOrderLog();
    log.setEnterpriseId(o.getEnterpriseId());
    log.setOrderId(o.getId());
    log.setOperator(username);

    String content = "修改供应商为: " + supplier.getName();
    log.setContent(content);
    em.persist(log);

    return 1;
  }

  @Override
  public int updateTrainOrderPrice(int enterpriseId, String username, int orderId, double price, double cost, double serviceCharge, double totalAmount, double totalCost, double profit) {
    int retCode = em.createQuery("update TrainOrder o set o.price = :price, o.cost = :cost, o.serviceCharge = :serviceCharge, " +
            "o.totalAmount = :totalAmount, o.totalCost = :totalCost, o.profit = :profit, o.lastUpdate = :lastUpdate " +
            "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("price", price)
            .setParameter("cost", cost)
            .setParameter("serviceCharge", serviceCharge)
            .setParameter("totalAmount", totalAmount)
            .setParameter("totalCost", totalCost)
            .setParameter("profit", profit)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode == 1) {
      OperationLog log = OperationLogFactory.createTrainOrderLog();
      log.setEnterpriseId(enterpriseId);
      log.setOrderId(orderId);
      log.setOperator(username);
      log.setContent("修改价格为：" + price + ", " + cost + ", " + serviceCharge);
      em.persist(log);
    }

    return retCode;
  }

  @Override
  public int updateTrainOrderPassenger(int enterpriseId, String username, int orderId, TrainOrderPassengerDto psgDto) {
    int retCode = em.createQuery("update TrainOrderPassenger o set o.name = :name, o.mobile = :mobile, o.coachNo = :coachNo, o.seatNo = :seatNo, o.lastUpdate = :lastUpdate " +
            "where o.id = :id and o.enterpriseId = :enterpriseId and o.orderInfo.id = :orderId")
            .setParameter("id", psgDto.getId())
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("orderId", orderId)
            .setParameter("name", psgDto.getName())
            .setParameter("mobile", psgDto.getMobile())
            .setParameter("coachNo", psgDto.getCoachNo())
            .setParameter("seatNo", psgDto.getSeatNo())
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode == 1) {
      OperationLog log = OperationLogFactory.createTrainOrderLog();
      log.setEnterpriseId(enterpriseId);
      log.setOrderId(orderId);
      log.setOperator(username);
      log.setContent("修改乘车为：" + psgDto.getName() + ", " + psgDto.getMobile() + ", " + psgDto.getCoachNo() + ", " + psgDto.getSeatNo());
      em.persist(log);
    }

    return retCode;
  }

  /**
   * 修改酒店订单的支付方式
   *
   * @param enterpriseId
   * @param username
   * @param orderId
   * @param paymentMethodId
   * @return
   */
  @Override
  public int updateTrainOrderPaymentMethod(int enterpriseId, String username, int orderId, int paymentMethodId) {
    TrainOrder o = em.find(TrainOrder.class, orderId);
    if (o.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("TrainOrder is not existed.");
    }

    PaymentMethod pm = em.find(PaymentMethod.class, paymentMethodId);
    if (pm.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("PaymentMethod is not existed.");
    }

    o.setPaymentMethod(pm);
    em.merge(o);

    OperationLog log = OperationLogFactory.createTrainOrderLog();
    log.setEnterpriseId(o.getEnterpriseId());
    log.setOrderId(o.getId());
    log.setOperator(username);

    String content = "修改支付方式为: " + pm.getName();
    log.setContent(content);
    em.persist(log);

    return 1;
  }

  @Override
  public int processTrainOrder(int enterpriseId, String username, int orderId) {
    int retCode = em.createQuery("update TrainOrder o set o.status = :newStatus, o.ticketer = :ticketer, o.lastUpdate = :lastUpdate " +
            "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", TrainOrder.STATUS_WAITING)
            .setParameter("newStatus", TrainOrder.STATUS_PROCESSING)
            .setParameter("ticketer", username)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("update status failed.");
    }

    OperationLog log = OperationLogFactory.createTrainOrderLog();
    log.setEnterpriseId(enterpriseId);
    log.setOrderId(orderId);
    log.setOperator(username);

    String content = "处理订单";
    log.setContent(content);
    em.persist(log);

    return 1;
  }

  @Override
  public int finishTrainOrder(int enterpriseId, String username, int orderId, String remark) {
    int retCode = em.createQuery("update TrainOrder o set o.status = :newStatus, o.lastUpdate = :lastUpdate " +
            "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", TrainOrder.STATUS_PROCESSING)
            .setParameter("newStatus", TrainOrder.STATUS_DONE)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("update status failed.");
    }

    OperationLog log = OperationLogFactory.createTrainOrderLog();
    log.setEnterpriseId(enterpriseId);
    log.setOrderId(orderId);
    log.setOperator(username);

    String content = "订单处理完毕(" + remark + ")";
    log.setContent(content);
    em.persist(log);

    return 1;
  }

  @Override
  public List<OperationLog> searchTrainOrderHistory(int enterpriseId, int orderId) {
    return searchOrderHistory(enterpriseId, OPERATING_OBJECT_TRAIN_ORDER, orderId);
  }

  /**
   * 取消酒店订单
   *
   * @param enterpriseId
   * @param username
   * @param hotelOrderId
   * @param remark
   * @return
   */
  @Override
  public int cancelTrainOrder(int enterpriseId, String username, int hotelOrderId, String remark) {
    int retCode = em.createQuery("update TrainOrder o set o.status = :newStatus, o.lastUpdate = :lastUpdate " +
            "where o.id = :id and o.enterpriseId = :enterpriseId and o.status in (:oldStatus1, :oldStatus2)")
            .setParameter("id", hotelOrderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus1", TrainOrder.STATUS_WAITING)
            .setParameter("oldStatus2", TrainOrder.STATUS_PROCESSING)
            .setParameter("newStatus", TrainOrder.STATUS_CANCELED)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("To cancel hotel order failed.");
    }

    OperationLog log = OperationLogFactory.createTrainOrderLog();
    log.setEnterpriseId(enterpriseId);
    log.setOperator(username);
    log.setOrderId(hotelOrderId);
    log.setContent("取消订单： " + remark);
    em.persist(log);

    return retCode;
  }

  /**
   * 回滚酒店订单
   *
   * @param enterpriseId
   * @param username
   * @param orderId
   * @return
   */
  @Override
  public int rollbackTrainOrder(int enterpriseId, String username, int orderId, final String remark) {
    int retCode = em.createQuery("update TrainOrder o set o.status = :newStatus, o.lastUpdate = :lastUpdate " +
            "where o.id = :id and o.enterpriseId = :enterpriseId and o.status in (:oldStatus1)")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus1", TrainOrder.STATUS_DONE)
            .setParameter("newStatus", TrainOrder.STATUS_PROCESSING)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("To rollback hotel order failed.");
    }

    OperationLog log = OperationLogFactory.createTrainOrderLog();
    log.setEnterpriseId(enterpriseId);
    log.setOperator(username);
    log.setOrderId(orderId);
    log.setContent("订单回滚: " + remark);
    em.persist(log);

    return retCode;
  }

  @Override
  public List<TrainOrder> searchUnpaidOrders(int max) {
    return em.createQuery("select o from TrainOrder o " +
            "where (o.payStatus is null or o.payStatus <> 2) " +
            "and o.status = :status " +
            "and o.createTime >= :createTime " +
            "order by o.id desc", TrainOrder.class)
            .setParameter("status", TrainOrder.STATUS_DONE)
            .setParameter("createTime", getMinBeginDate())
            .setMaxResults(max)
            .getResultList();
  }

  @Override
  public int setOrderWriteOff(int enterpriseId, String username, int orderId, String remark) {
    int retCode = em.createQuery("update TrainOrder o set " +
            "o.payStatus = 2 " +
            "where o.id = :id and o.enterpriseId = :enterpriseId and (o.payStatus is null or o.payStatus <> 2)")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .executeUpdate();

    if (retCode == 1) {
      OperationLog log = createTrainOrderLog(enterpriseId, orderId, username, "已销账：" + remark);
      em.persist(log);
    }

    return retCode;
  }

  private OperationLog createTrainOrderLog(int enterpriseId, int orderId, String username, String s) {
    OperationLog log = OperationLogFactory.createTrainOrderLog();
    log.setEnterpriseId(enterpriseId);
    log.setOrderId(orderId);
    log.setOperator(username);
    log.setContent(s);

    return log;
  }
}
